/*
 Copyright (c) [Year] [name of copyright holder]
 [Software Name] is licensed under Mulan PSL v2.
 You can use this software according to the terms and conditions of the Mulan
 PSL v2.
 You may obtain a copy of Mulan PSL v2 at:
          http://license.coscl.org.cn/MulanPSL2
 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 See the Mulan PSL v2 for more details.
*/

#include "PasteConfig.hpp"

// 构造函数：初始化配置信息
PasteConfig::PasteConfig() {
    // 尝试打开配置文件
    std::ifstream configFile("config.json");

    // 如果配置文件无法打开，则创建默认配置
    if (!configFile.is_open()) {
        CreateDefaultConfig();
    }

    // 读取配置文件内容到字符串
    std::istreambuf_iterator<char> begin(configFile), end;
    std::string configStr(begin, end);
    configFile.close();

    // 如果配置文件内容为空，则创建默认配置
    if (configStr.empty()) {
        CreateDefaultConfig();
    }

    // 解析配置文件的JSON内容
    configJson = cJSON_Parse(configStr.c_str());
    // 如果JSON解析失败，抛出异常
    if (configJson == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        std::string errorMsg = "[PasteConfig] 无法解析JSON文件";
        // 添加具体的错误信息
        if (error_ptr != nullptr && *error_ptr != '\0') {
            errorMsg += ": " + std::string(error_ptr);
        }
        throw std::runtime_error(errorMsg);
    }
}

PasteConfig::~PasteConfig() {}

/**
 * @brief 获取配置项的布尔值
 *
 * 本函数通过给定的键从JSON配置中获取对应的布尔值配置项，并将其值作为布尔类型返回。
 * 如果键或配置JSON为空，或者配置项不存在，或者配置项类型不是布尔类型，则抛出异常。
 *
 * @param key 配置项的键名，用于在JSON对象中查找对应的配置项
 * @return 配置项的布尔值
 */
bool PasteConfig::GetBoolValue(const char *key) const {
    // 检查参数有效性
    if (key == nullptr) {
        throw std::invalid_argument("[PasteConfig::GetBoolValue] 参数无效: key 为 NULL");
    }

    // 获取指定键的配置项指针
    cJSON *itemPtr = cJSON_GetObjectItem(configJson, key);

    // 检查配置项是否存在
    if (itemPtr == nullptr) {
        throw std::runtime_error("[PasteConfig::GetBoolValue] 无法获取配置项: " + std::string(key));
    }

    // 检查配置项类型是否为布尔类型
    if (!cJSON_IsBool(itemPtr)) {
        throw std::invalid_argument("[PasteConfig::GetBoolValue] 配置项类型错误: " + std::string(key) + " 不是布尔类型");
    }

    // 返回配置项的布尔值
    return itemPtr->valueint != 0;
}

/**
 * @brief 获取配置项的整数值
 *
 * 本函数通过给定的键从JSON配置中获取对应的整数值配置项，并将其值作为整数类型返回。
 * 如果键或配置JSON为空，或者配置项不存在，或者配置项类型不是整数类型，则抛出异常。
 *
 * @param key 配置项的键名，用于在JSON对象中查找对应的配置项
 * @return 配置项的整数值
 */
int PasteConfig::GetIntValue(const char *key) const {
    // 检查传入的键名是否为nullptr
    if (key == nullptr) {
        throw std::invalid_argument("[PasteConfig::GetIntValue] 参数无效: key 为 NULL");
    }

    // 从配置的JSON对象中获取指定键名的项指针
    cJSON *itemPtr = cJSON_GetObjectItem(configJson, key);

    // 检查指定的配置项是否存在
    if (itemPtr == nullptr) {
        throw std::runtime_error("[PasteConfig::GetIntValue] 无法获取配置项: " + std::string(key));
    }

    // 检查配置项是否为整数类型
    if (!cJSON_IsNumber(itemPtr)) {
        throw std::runtime_error("[PasteConfig::GetIntValue] 配置项类型错误: " + std::string(key) + " 不是整数类型");
    }

    // 返回配置项的整数值
    return itemPtr->valueint;
}

/**
 * @brief 根据键获取字符串配置值
 *
 * 该函数通过给定的键从配置的JSON对象中获取对应的字符串值如果键为NULL，
 * 或者配置项不存在，或者配置项不是字符串类型，则会抛出相应的异常如果配置项
 * 存在但值为空字符串，则返回空字符串
 *
 * @param key 配置项的键，不能为空
 * @return std::string 对应的字符串值，如果为空则返回空字符串
 */
std::string PasteConfig::GetStrValue(const char *key) const {
    // 检查键是否为NULL，如果为NULL则抛出无效参数异常
    if (key == nullptr) {
        throw std::invalid_argument("[PasteConfig::GetStrValue] 参数无效: key 为 NULL");
    }

    // 从configJson中获取指定键的项指针
    cJSON *itemPtr = cJSON_GetObjectItem(configJson, key);

    // 检查项指针是否为NULL，如果为NULL则抛出运行时错误
    if (itemPtr == NULL) {
        throw std::runtime_error("[PasteConfig::GetStrValue] 无法获取配置项: " + std::string(key));
    }

    // 检查项是否为字符串类型，如果不是则抛出运行时错误
    if (!cJSON_IsString(itemPtr)) {
        throw std::runtime_error("[PasteConfig::GetStrValue] 配置项不是字符串类型: " + std::string(key));
    }

    // 检查项的值字符串是否为NULL，如果是则返回空字符串
    if (itemPtr->valuestring == nullptr) {
        return "";
    }

    // 返回项的值字符串
    return itemPtr->valuestring;
}

// 创建默认配置文件
void PasteConfig::CreateDefaultConfig() {
    // 创建一个空的JSON对象
    cJSON *defaultConfig = cJSON_CreateObject();

    // 为配置项添加默认值
    // 数据库主机地址
    cJSON_AddStringToObject(defaultConfig, "DB_HOST", "localhost");
    // 数据库用户名
    cJSON_AddStringToObject(defaultConfig, "DB_USER", "root");
    // 数据库密码
    cJSON_AddStringToObject(defaultConfig, "DB_PASSWD", "123456");
    // 数据库
    cJSON_AddStringToObject(defaultConfig, "DB_DB", "test1");

    cJSON_AddStringToObject(defaultConfig, "directory", "D:\\img\\");
    cJSON_AddStringToObject(defaultConfig, "textExtension", ".txt");
    cJSON_AddStringToObject(defaultConfig, "imageExtension", ".jpg");
    cJSON_AddNumberToObject(defaultConfig, "baseNumber", 1000);
    cJSON_AddBoolToObject(defaultConfig, "isManual", true);
    cJSON_AddStringToObject(defaultConfig, "categoryName", "纽扣");
    // cJSON_AddBoolToObject(defaultConfig, "saveInitialNumBmp", true);
    // cJSON_AddBoolToObject(defaultConfig, "saveInitialNumJpg", true);
    // cJSON_AddBoolToObject(defaultConfig, "saveNumJpg", true);
    // cJSON_AddBoolToObject(defaultConfig, "saveNoNumJpg", true);
    // cJSON_AddBoolToObject(defaultConfig, "saveSplitNumJpg", true);
    // cJSON_AddBoolToObject(defaultConfig, "saveSplitNoNumJpg", true);

    // 尝试打开或创建配置文件
    std::ofstream outFile("config.json");
    if (!outFile.is_open()) {
        // 如果文件无法打开，则抛出异常
        throw std::runtime_error("[PasteConfig::CreateDefaultConfig] 无法打开文件: config.json");
    }

    // 将JSON对象转换为字符串
    char *configStr = cJSON_Print(defaultConfig);
    if (!configStr) {
        // 如果无法生成配置字符串，则抛出异常
        throw std::runtime_error("[PasteConfig::CreateDefaultConfig] 无法生成配置字符串");
    }

    // 将配置字符串写入文件
    outFile << configStr;
    outFile.close();

    // 释放配置字符串内存
    cJSON_free(configStr);
    // 输出成功创建配置文件的消息
    std::cout << "已在此程序目录下创建默认配置文件: config.json" << std::endl;
    std::cout << "请修改配置文件后再运行" << std::endl;
    std::cout << "按任意键退出..." << std::endl;
    std::cin;
    // 直接退出程序
    exit(0);
}
